package Mapa;
import java.io.*;
import Logica_Moviemientos.Gravedad;
import Objetos.*;
import TDA_Lista.*;
import Personajes.*; 

public class Mapa {
	protected SLinkedList<Personaje> enemigos;
	protected SLinkedList<Objeto> objetos;
	protected Mario mario;
	protected Gravedad gravedad;
	protected int ancho;
	protected int alto;
	
	/**Constructor. */
	public Mapa(){
		objetos = new SLinkedList<Objeto>();
		enemigos = new SLinkedList<Personaje>();
		ancho = 300 * 32;
		alto = 25 * 32;
		cargarLogicaNivel();
		agregarMario();
		cargarGravedad();
	}
	
	//PARA OBTENER LAS DIMENSIONES DEL MAPA
	
	/**Retorna el alto del mapa. */
	public int obtenerAncho(){
		return ancho;
	}
	
	/**Retorna el ancho del mapa. */
	public int obtenerAlto(){
		return alto;
	}
	
	//PARA AGREGAR OBJETOS Y PERSONAJES AL MAPA LOGICO
	
	/**Carga todos los objetos logicos en el mapa desde un archivo. */
	private void cargarLogicaNivel(){
		InputStream is = getClass().getResourceAsStream("/Niveles/nivel_01.txt");
		BufferedReader br = new BufferedReader(new InputStreamReader(is));
		CreatorByFile cbf = new CreatorByFile(br);
		agregarObjetos(cbf);
	}
	
	/**Metodo auxiliar para asignar las posiciones de los 
	 * objetos y agregarlos al mapa.*/
	private void agregarObjetos( CreatorByFile c ){
		for( int i = 0; i < 25; i++ ){
			for( int j = 0; j < 300; j++ ){
				Objeto e = c.getMatriz().getObjeto(i, j);
				if(e != null){
					e.setPosicion(j*32, i*32);
					e.setBounds(j*32, i*32,32,32);
					objetos.addLast(e);
				}
			}
		}
	}
	
	/**Agraga el mario logico en un archivo. */
	private void agregarMario(){
		mario = new Mario();
		mario.setPosicion(32, 17*32);
		mario.setBounds(32, 17*32, 32, 32);
	}
	
	//PARA LA GRAVEDAD
	
	/**Agrega gravedad al mapa. */
	private void cargarGravedad(){
		gravedad = new Gravedad(this);
		gravedad.start();
	}
	
	public void setGravedad(boolean b){
		gravedad.setGravedad(b);
	}
	
	//PARA OBTENER OBJETOS O PERSONAJES DEL MAPA
	
	/**Retorna el personaje mario. */
	public Mario obtenerMario(){
		return mario;
	}
	
	/**Retorna todos los objetos del mapa. */
	public SLinkedList<Objeto> obtenerObjetos(){
		return objetos;
	}
	
	/**Retorna todos los enemigos del mapa. */
	public SLinkedList<Personaje> obtenerEnemigos(){
		return enemigos;
	}
	
	//PARA VERIFICAR COLISIONES ENTRE PERSONAJES Y OBJETOS
	
	/**Retorna true en el caso de que el personaje encuentre 
	 * un obstaculo a la izquierda, falso en caso contrario. */
	public boolean colisionAIzquierda(Personaje p){
		boolean toReturn = false;
		for(Objeto o : obtenerObjetos()){
			if(o.obtenerX() + 32 == p.obtenerX()){
				if((o.obtenerY() - p.obtenerY() < 32)&&(o.obtenerY() - p.obtenerY() >= 0)) toReturn = true;
				else if((p.obtenerY() - o.obtenerY() < 32)&&(p.obtenerY() - o.obtenerY() >= 0)) toReturn = true;
			}
		}
		return toReturn;
	}
	
	/**Retorna true en el caso de que el personaje encuentre 
	 * un obstaculo a la derecha, falso en caso contrario. */
	public boolean colisionADerecha(Personaje p){
		boolean toReturn = false;
		for(Objeto o : obtenerObjetos()){
			if(o.obtenerX() == p.obtenerX() + 32){
				if((o.obtenerY() - p.obtenerY() < 32)&&(o.obtenerY() - p.obtenerY() >= 0)) toReturn = true;
				else if((p.obtenerY() - o.obtenerY() < 32)&&(p.obtenerY() - o.obtenerY() >= 0))	toReturn = true;
			}
		}
		return toReturn;
	}
	
	/**Retorna true en el caso de que el personaje encuentre un
	 * obstaculo arriba de el, falso en caso contrario. */
	public boolean colisionArriba(Personaje p){
		boolean hayObstaculo = false;
		for(Objeto o : obtenerObjetos()){
			boolean A1 = p.obtenerX() - o.obtenerX() < 32;
			boolean A2 = p.obtenerX() - o.obtenerX() >= 0;
			boolean B1 = o.obtenerX() - p.obtenerX() < 32;
			boolean B2 = o.obtenerX() - p.obtenerX() >= 0;
			if((A1 && A2)||(B1 && B2)){
				boolean C = o.obtenerY() + 32  == p.obtenerY();
				if(C){
					hayObstaculo = true;
					break;
				}
			}
		}
		return hayObstaculo;
	}
	
	/**Retorna true en el caso de que el personaje encuentre un
	 * obstaculo abajo de el, falso en caso contrario. */
	public boolean colisionAbajo(Personaje p){
		boolean hayObstaculo = false;
		for(Objeto o : obtenerObjetos()){
			boolean A1 = p.obtenerX() - o.obtenerX() < 32;
			boolean A2 = p.obtenerX() - o.obtenerX() >= 0;
			boolean B1 = o.obtenerX() - p.obtenerX() < 32;
			boolean B2 = o.obtenerX() - p.obtenerX() >= 0;
			if((A1 && A2)||(B1 && B2)){
				boolean C = o.obtenerY() == p.obtenerY() + 32;
				if(C){
					hayObstaculo = true;
					break;
				}
			}
		}
		return hayObstaculo;
	}
	
	//PARA REMOVER OBJETOS O PERSONAJES DEL MAPA
	
	/**Remueve un objeto del mapa logico. */
	public void removerObjeto(Position<Objeto> o){
		try {
			objetos.remove(o);
		} catch (InvalidPositionException e) {
			System.out.println("Mapa :: removerObjeto(o)");
		}
	}
	
	/**Remueve un enemigo del mapa logico. */
	public void removerEnemigo(Position<Personaje> p){
		try {
			enemigos.remove(p);
		} catch (InvalidPositionException e) {
			System.out.println("Mapa :: removerPersonaje(p)");
		}	
	}
	
}
